// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com

// ReSharper disable CheckNamespace
// ReSharper disable CommentTypo
// ReSharper disable CompareOfFloatsByEqualityOperator
// ReSharper disable InconsistentNaming

/* ScaleState.cs --
 * Ars Magna project, http://arsmagna.ru
 */

#region Using directives

using System;

#endregion

#nullable enable

namespace AM.Drawing.Charting;

/// <summary>
/// A class that captures an <see cref="Axis"/> scale range.
/// </summary>
/// <remarks>This structure is used by the <see cref="ZoomState"/> class to store
/// <see cref="Axis"/> scale range settings in a collection for later retrieval.
/// The class stores the <see cref="Scale.Min"/>, <see cref="Scale.Max"/>,
/// <see cref="Scale.MinorStep"/>, and <see cref="Scale.MajorStep"/> properties, along with
/// the corresponding auto-scale settings: <see cref="Scale.MinAuto"/>,
/// <see cref="Scale.MaxAuto"/>, <see cref="Scale.MinorStepAuto"/>,
/// and <see cref="Scale.MajorStepAuto"/>.</remarks>
public class ScaleState
    : ICloneable
{
    /// <summary>
    /// The axis range data for <see cref="Scale.Min"/>, <see cref="Scale.Max"/>,
    /// <see cref="Scale.MinorStep"/>, and <see cref="Scale.MajorStep"/>
    /// </summary>
    private double _min, _minorStep, _majorStep, _max;

    /// <summary>
    /// The status of <see cref="Scale.MinAuto"/>,
    /// <see cref="Scale.MaxAuto"/>, <see cref="Scale.MinorStepAuto"/>,
    /// and <see cref="Scale.MajorStepAuto"/>
    /// </summary>
    private bool _minAuto,
        _minorStepAuto,
        _majorStepAuto,
        _maxAuto,
        _formatAuto,
        _magAuto;

    /// <summary>
    /// The status of <see cref="Scale.MajorUnit"/> and <see cref="Scale.MinorUnit"/>
    /// </summary>
    private DateUnit _minorUnit, _majorUnit;

    private string _format;
    private int _mag;

    /// <summary>
    /// Construct a <see cref="ScaleState"/> from the specified <see cref="Axis"/>
    /// </summary>
    /// <param name="axis">The <see cref="Axis"/> from which to collect the scale
    /// range settings.</param>
    public ScaleState
        (
            Axis? axis
        )
    {
        _format = null!;

        if (axis is { Scale: not null })
        {
            _min = axis.Scale._min;
            _minorStep = axis.Scale._minorStep;
            _majorStep = axis.Scale._majorStep;
            _max = axis.Scale._max;
            _majorUnit = axis.Scale._majorUnit;
            _minorUnit = axis.Scale._minorUnit;

            _format = axis.Scale._format!;
            _mag = axis.Scale?._mag ?? 0;

            //this.numDec = axis.NumDec;

            _minAuto = axis.Scale!._minAuto;
            _majorStepAuto = axis.Scale._majorStepAuto;
            _minorStepAuto = axis.Scale._minorStepAuto;
            _maxAuto = axis.Scale._maxAuto;

            _formatAuto = axis.Scale._formatAuto;
            _magAuto = axis.Scale._magAuto;
        }
    }

    /// <summary>
    /// The Copy Constructor
    /// </summary>
    /// <param name="rhs">The <see cref="ScaleState"/> object from which to copy</param>
    public ScaleState
        (
            ScaleState rhs
        )
    {
        _min = rhs._min;
        _majorStep = rhs._majorStep;
        _minorStep = rhs._minorStep;
        _max = rhs._max;
        _majorUnit = rhs._majorUnit;
        _minorUnit = rhs._minorUnit;

        _format = rhs._format;
        _mag = rhs._mag;

        _minAuto = rhs._minAuto;
        _majorStepAuto = rhs._majorStepAuto;
        _minorStepAuto = rhs._minorStepAuto;
        _maxAuto = rhs._maxAuto;

        _formatAuto = rhs._formatAuto;
        _magAuto = rhs._magAuto;
    }

    /// <summary>
    /// Implement the <see cref="ICloneable" /> interface in a typesafe manner by just
    /// calling the typed version of <see cref="Clone" />
    /// </summary>
    /// <returns>A deep copy of this object</returns>
    object ICloneable.Clone()
    {
        return Clone();
    }

    /// <summary>
    /// Typesafe, deep-copy clone method.
    /// </summary>
    /// <returns>A new, independent copy of this class</returns>
    public ScaleState Clone()
    {
        return new ScaleState (this);
    }

    /// <summary>
    /// Copy the properties from this <see cref="ScaleState"/> out to the specified <see cref="Axis"/>.
    /// </summary>
    /// <param name="axis">The <see cref="Axis"/> reference to which the properties should be
    /// copied</param>
    public void ApplyScale (Axis axis)
    {
        axis.Scale!._min = _min;
        axis.Scale._majorStep = _majorStep;
        axis.Scale._minorStep = _minorStep;
        axis.Scale._max = _max;
        axis.Scale._majorUnit = _majorUnit;
        axis.Scale._minorUnit = _minorUnit;

        axis.Scale._format = _format;
        axis.Scale._mag = _mag;

        // The auto settings must be made after the min/step/max settings, since setting those
        // properties actually affects the auto settings.
        axis.Scale._minAuto = _minAuto;
        axis.Scale._minorStepAuto = _minorStepAuto;
        axis.Scale._majorStepAuto = _majorStepAuto;
        axis.Scale._maxAuto = _maxAuto;

        axis.Scale._formatAuto = _formatAuto;
        axis.Scale._magAuto = _magAuto;
    }

    /// <summary>
    /// Determine if the state contained in this <see cref="ScaleState"/> object is different from
    /// the state of the specified <see cref="Axis"/>.
    /// </summary>
    /// <param name="axis">The <see cref="Axis"/> object with which to compare states.</param>
    /// <returns>true if the states are different, false otherwise</returns>
    public bool IsChanged (Axis axis)
    {
        return axis.Scale!._min != _min ||
               axis.Scale._majorStep != _majorStep ||
               axis.Scale._minorStep != _minorStep ||
               axis.Scale._max != _max ||
               axis.Scale._minorUnit != _minorUnit ||
               axis.Scale._majorUnit != _majorUnit ||
               axis.Scale._minAuto != _minAuto ||
               axis.Scale._minorStepAuto != _minorStepAuto ||
               axis.Scale._majorStepAuto != _majorStepAuto ||
               axis.Scale._maxAuto != _maxAuto;
    }
}
